﻿using System;
using System.ComponentModel;
using System.IO;
using System.Media;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;

namespace DevelopTool.Common
{
    /// <summary>
    /// Wav声音播放辅助类
    /// </summary>
    public static class WavPlayerHelper
    {
        /// <summary>
        /// 指示如何调用音频方法时，播放声音。
        /// </summary>
        public enum AudioPlayMode
        {
            /// <summary>
            /// 播放声音，并等待。
            /// </summary>
            WaitToComplete,

            /// <summary>
            /// 在后台播放声音。
            /// </summary>
            Background,

            /// <summary>
            /// 后台循环播放，直到调用stop方法停止。
            /// </summary>
            BackgroundLoop
        }

        private static SoundPlayer _SoundPlayer;

        #region Methods

        private static void InternalStop(SoundPlayer sound)
        {
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
            try
            {
                sound.Stop();
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
        }

        /// <summary>播放。wav声音文件。</summary>
        /// <param name="location">String，包含声音文件的名称 </param>
        /// <PermissionSet><IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlThread" /><IPermission class="System.Net.WebPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /></PermissionSet>
        public static void Play(string location)
        {
            Play(location, AudioPlayMode.Background);
        }

        /// <summary>
        /// 播放。wav声音文件.
        /// </summary>
        /// <param name="location">AudioPlayMode枚举模式播放声音。默认情况下，AudioPlayMode.Background。</param>
        /// <param name="playMode">String，包含声音文件的名称</param>
        public static void Play(string location, AudioPlayMode playMode)
        {
            ValidateAudioPlayModeEnum(playMode, "playMode");
            if (string.IsNullOrEmpty(location) || !File.Exists(location)) return;
            SoundPlayer player1 = new SoundPlayer(location);
            Play(player1, playMode);
        }


        public static void Play(Stream stream, AudioPlayMode playMode)
        {
            ValidateAudioPlayModeEnum(playMode, "playMode");
            SoundPlayer player1 = new SoundPlayer(stream);
            Play(player1, playMode);
        }

        /// <summary>
        /// 停止在后台播放声音。
        /// </summary>
        /// <filterpriority>1</filterpriority>
        public static void Stop()
        {
            SoundPlayer player1 = new SoundPlayer();
            InternalStop(player1);
        }

        private static void Play(SoundPlayer sound, AudioPlayMode mode)
        {
            if (_SoundPlayer != null)
            {
                InternalStop(_SoundPlayer);
            }
            _SoundPlayer = sound;
            switch (mode)
            {
                case AudioPlayMode.WaitToComplete:
                    _SoundPlayer.PlaySync();
                    return;

                case AudioPlayMode.Background:
                    _SoundPlayer.Play();
                    return;

                case AudioPlayMode.BackgroundLoop:
                    _SoundPlayer.PlayLooping();
                    return;
            }
        }


        private static void ValidateAudioPlayModeEnum(AudioPlayMode value, string paramName)
        {
            if ((value < AudioPlayMode.WaitToComplete) || (value > AudioPlayMode.BackgroundLoop))
            {
                throw new InvalidEnumArgumentException(paramName, (int)value, typeof(AudioPlayMode));
            }
        }

        #endregion Methods
    }

    /// <summary>
    /// Wav声音播放辅助类
    /// </summary>
    public static class SytemSoundHelper
    {
        // 导入PlaySound函数  
        [DllImport("winmm.dll")]
        public static extern bool PlaySound(string pszSound, IntPtr hmod, uint fdwSound);


        /// <summary>
        /// 播放系统声音  
        /// </summary>
        /// <param name="soundAlias">使用系统声音的别名</param>
        /// <param name="loop">是否循环播放</param>
        public static void PlaySystemSound(string soundAlias = "SystemQuestion", bool loop = true)
        {
            if (loop)
                PlaySound(soundAlias, IntPtr.Zero, SoundFlags.SND_LOOP | SoundFlags.SND_ALIAS | SoundFlags.SND_ASYNC);
            else
                PlaySound(soundAlias, IntPtr.Zero, SoundFlags.SND_ALIAS | SoundFlags.SND_ASYNC);
        }
    }


    /// <summary>
    /// 包含 PlaySound 函数使用的fdwSound 标志位定义
    /// </summary>
    public static class SoundFlags
    {
        /// <summary>
        /// 同步播放声音。调用 PlaySound 函数后，函数会等待直到声音播放完毕才返回。
        /// </summary>
        public const uint SND_SYNC = 0x0000;

        /// <summary>
        /// 异步播放声音。调用 PlaySound 函数后，函数会立即返回，声音将在后台播放。
        /// </summary>
        public const uint SND_ASYNC = 0x0001;

        /// <summary>
        /// 如果没有找到指定的声音文件或资源，则不播放系统默认声音。
        /// </summary>
        public const uint SND_NODEFAULT = 0x0002;

        /// <summary>
        /// 指示 lpszSound 参数是一个指向声音数据的内存指针，而不是文件名或资源标识符。
        /// 注意：这个标志在.NET环境中不常用，因为.NET通常不直接处理内存指针。
        /// </summary>
        public const uint SND_MEMORY = 0x0004;

        /// <summary>
        /// 循环播放声音。声音将重复播放，直到明确停止。
        /// 通常与 SND_ASYNC 一起使用。
        /// </summary>
        public const uint SND_LOOP = 0x0008;

        /// <summary>
        /// 如果指定的声音已经在播放，则不中断当前播放的声音。
        /// </summary>
        public const uint SND_NOSTOP = 0x0010;

        /// <summary>
        /// 如果声音驱动程序正忙，则不播放声音，并且函数立即返回。
        /// 这个标志在.NET环境中可能不常用，因为.NET提供了更高级的异常处理机制。
        /// </summary>
        public const uint SND_NOWAIT = 0x00002000;

        /// <summary>
        /// 指示 lpszSound 参数是一个系统声音事件的别名，如 "SystemAsterisk"、"SystemExclamation" 等。
        /// </summary>
        public const uint SND_ALIAS = 0x00010000;

        /// <summary>
        /// 指示 lpszSound 参数是一个声音文件的名称（包括路径）。
        /// </summary>
        public const uint SND_FILENAME = 0x00020000;

        /// <summary>
        /// 指示 lpszSound 参数是一个资源标识符，hmod 参数必须提供包含该资源的模块的句柄。
        /// 在.NET中，这通常意味着你需要使用 P/Invoke 来调用非托管的资源。
        /// </summary>
        public const uint SND_RESOURCE = 0x00040000 | 0x0010; // 注意：SND_RESOURCE 实际上包含了 SND_NOSTOP 的值

        /// <summary>
        /// 停止所有与调用任务（线程）有关的声音。
        /// 如果 lpszSound 为 NULL，则停止所有声音。
        /// </summary>
        public const uint SND_PURGE = 0x0040;

        /// <summary>
        /// 使用应用程序指定的关联来播放声音。
        /// 这通常与 SND_ALIAS 一起使用，以播放与特定应用程序关联的系统声音。
        /// </summary>
        public const uint SND_APPLICATION = 0x0080;
    }
}
